import { RewardFactory } from './factory';
import {
  REWARD_TYPE,
  type RewardType,
  type ExpReward,
  type ItemReward,
  type SpiritStoneReward,
  type Reward,
  type RewardPoolConfig,
  type SectContributionReward,
} from './type';

/**
 * 奖励工具类
 */
export class RewardUtils {
  /**
   * 合并相同类型的奖励
   * - 经验值奖励：累加数量
   * - 物品奖励：相同物品 ID 的累加数量
   */
  static mergeRewards(rewards: Reward[]): Reward[] {
    const mergedRewards = new Map<string, Reward>();

    for (const reward of rewards) {
      const key = this.getRewardKey(reward);

      if (mergedRewards.has(key)) {
        // 合并现有奖励
        const existingReward = mergedRewards.get(key);
        if (existingReward) {
          mergedRewards.set(
            key,
            this.mergeSingleReward(existingReward, reward)
          );
        }
      } else {
        // 新增奖励
        mergedRewards.set(key, { ...reward });
      }
    }

    return Array.from(mergedRewards.values());
  }

  /**
   * 获取奖励的唯一标识键
   */
  private static getRewardKey(reward: Reward): string {
    switch (reward.type) {
      case REWARD_TYPE.EXP:
        return `EXP`;
      case REWARD_TYPE.ITEM:
        return `ITEM_${reward.itemId}`;
      case REWARD_TYPE.SPIRIT_STONE:
        return `SPIRIT_STONE`;
      case REWARD_TYPE.SECT_CONTRIBUTION:
        return `SECT_CONTRIBUTION`;
      default:
        // 使用类型断言处理未知类型
        const exhaustiveCheck: never = reward;
        throw new Error(`未知的奖励类型: ${exhaustiveCheck}`);
    }
  }

  /**
   * 合并两个相同类型的奖励
   */
  private static mergeSingleReward(
    existing: Reward,
    newReward: Reward
  ): Reward {
    // 类型不同，直接返回现有的（理论上不会发生，因为key已经相同）
    if (existing.type !== newReward.type) {
      return existing;
    }

    switch (existing.type) {
      case REWARD_TYPE.EXP:
        return this.mergeExpRewards(existing, newReward as ExpReward);
      case REWARD_TYPE.ITEM:
        return this.mergeItemRewards(existing, newReward as ItemReward);
      case REWARD_TYPE.SPIRIT_STONE:
        return this.mergeSpiritStoneRewards(
          existing,
          newReward as SpiritStoneReward
        );
      case REWARD_TYPE.SECT_CONTRIBUTION:
        return this.mergeSectContributionRewards(
          existing,
          newReward as SectContributionReward
        );
      default:
        // 使用类型断言处理未知类型
        const exhaustiveCheck: never = existing;
        throw new Error(`未知的奖励类型: ${exhaustiveCheck}`);
    }
  }

  /**
   * 合并经验值奖励
   */
  private static mergeExpRewards(
    existing: ExpReward,
    newReward: ExpReward
  ): ExpReward {
    return {
      ...existing,
      amount: existing.amount + newReward.amount,
      description: existing.description || newReward.description,
    };
  }

  /**
   * 合并物品奖励
   */
  private static mergeItemRewards(
    existing: ItemReward,
    newReward: ItemReward
  ): ItemReward {
    // 确保是相同物品ID
    if (existing.itemId !== newReward.itemId) {
      return existing;
    }

    return {
      ...existing,
      quantity: existing.quantity + newReward.quantity,
      description: existing.description || newReward.description,
      name: existing.name || newReward.name,
    };
  }

  /**
   * 合并灵石奖励
   */
  private static mergeSpiritStoneRewards(
    existing: SpiritStoneReward,
    newReward: SpiritStoneReward
  ): SpiritStoneReward {
    return {
      ...existing,
      quantity: existing.quantity + newReward.quantity,
      description: existing.description || newReward.description,
      name: existing.name || newReward.name,
    };
  }

  /**
   * 合并宗门贡献奖励
   */
  private static mergeSectContributionRewards(
    existing: SectContributionReward,
    newReward: SectContributionReward
  ): SectContributionReward {
    return {
      ...existing,
      quantity: existing.quantity + newReward.quantity,
      description: existing.description || newReward.description,
      name: existing.name || newReward.name,
    };
  }

  /**
   * 格式化显示合并后的奖励
   */
  static formatRewards(rewards: Reward[]): string {
    return rewards
      .map((reward) => {
        switch (reward.type) {
          case REWARD_TYPE.EXP:
            return `${reward.name}: ${reward.amount}`;
          case REWARD_TYPE.ITEM:
            return `${reward.name} × ${reward.quantity}`;
          case REWARD_TYPE.SPIRIT_STONE:
            return `${reward.name} × ${reward.quantity}`;
          case REWARD_TYPE.SECT_CONTRIBUTION:
            return `${reward.name}: ${reward.quantity}`;
          default:
            // 使用类型断言处理未知类型
            const exhaustiveCheck: never = reward;
            throw new Error(`未知的奖励类型: ${exhaustiveCheck}`);
        }
      })
      .join(', ');
  }

  /**
   * 过滤特定类型的奖励
   */
  static filterByType(rewards: Reward[], type: RewardType): Reward[] {
    return rewards.filter((reward) => reward.type === type);
  }

  /**
   * 获取奖励总价值（需要根据游戏逻辑扩展）
   */
  static calculateTotalValue(rewards: Reward[]): number {
    return rewards.reduce((total, reward) => {
      switch (reward.type) {
        case REWARD_TYPE.EXP:
          return total + (reward as ExpReward).amount;
        case REWARD_TYPE.ITEM:
          // 这里可以根据物品配置计算价值
          return total + (reward as ItemReward).quantity;
        case REWARD_TYPE.SPIRIT_STONE:
          return total + (reward as SpiritStoneReward).quantity;
        case REWARD_TYPE.SECT_CONTRIBUTION:
          return total + (reward as SectContributionReward).quantity;
        default:
          // 使用类型断言处理未知类型
          const exhaustiveCheck: never = reward;
          throw new Error(`未知的奖励类型: ${exhaustiveCheck}`);
      }
    }, 0);
  }

  /**
   * 安全的格式化方法，不会抛出错误
   */
  static formatRewardsSafe(rewards: Reward[]): string {
    return rewards
      .map((reward) => {
        try {
          switch (reward.type) {
            case REWARD_TYPE.EXP:
              return `${reward.name}: ${reward.amount}`;
            case REWARD_TYPE.ITEM:
              return `${reward.name} × ${reward.quantity}`;
            default:
              // 对于未知类型，使用备用格式化方式
              return `未知奖励 (${(reward as any).type})`;
          }
        } catch (error) {
          return `格式化错误`;
        }
      })
      .join(', ');
  }

  /**
   * 安全的获取奖励键方法
   */
  private static getRewardKeySafe(reward: Reward): string {
    try {
      return this.getRewardKey(reward);
    } catch (error) {
      // 如果出现未知类型，使用备用键生成方式
      return `UNKNOWN_${(reward as any).type || 'REWARD'}`;
    }
  }

  /**
   * 安全的合并奖励方法，不会因为未知类型而失败
   */
  static mergeRewardsSafe(rewards: Reward[]): Reward[] {
    const mergedRewards = new Map<string, Reward>();

    for (const reward of rewards) {
      const key = this.getRewardKeySafe(reward);

      if (mergedRewards.has(key)) {
        // 合并现有奖励
        const existingReward = mergedRewards.get(key);
        if (existingReward) {
          try {
            mergedRewards.set(
              key,
              this.mergeSingleReward(existingReward, reward)
            );
          } catch (error) {
            // 如果合并失败，保留原有奖励
            console.warn(`合并奖励失败: ${error}`);
            mergedRewards.set(key, existingReward);
          }
        }
      } else {
        // 新增奖励
        mergedRewards.set(key, { ...reward });
      }
    }

    return Array.from(mergedRewards.values());
  }
}

export class RewardDropCalculator {
  /**
   * 计算实际掉落数量
   */
  private static getActualDropCount(
    dropCount?: number | { min: number; max: number }
  ): number {
    if (dropCount === undefined) {
      return 1; // 默认掉落1个
    }

    if (typeof dropCount === 'number') {
      return dropCount;
    }

    // 范围随机
    return (
      Math.floor(Math.random() * (dropCount.max - dropCount.min + 1)) +
      dropCount.min
    );
  }

  /**
   * 从数组中随机选择指定数量的元素（不重复）
   */
  private static randomSelect<T>(array: T[], count: number): T[] {
    if (count <= 0) return [];
    if (count >= array.length) {
      return [...array];
    }

    const shuffled = [...array];
    for (let i = shuffled.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));

      const elementI = shuffled[i];
      const elementJ = shuffled[j];

      if (elementI !== undefined && elementJ !== undefined) {
        shuffled[i] = elementJ;
        shuffled[j] = elementI;
      }
    }
    return shuffled.slice(0, count);
  }

  /**
   * 概率策略：每个奖励独立判断是否掉落
   */
  private static probabilityBased(pool: RewardPoolConfig): Reward[] {
    const { rewards } = pool;
    const dropCount = this.getActualDropCount(pool.dropCount);

    // 先处理概率掉落
    const probabilisticRewards = rewards
      .filter((reward) => !reward.guaranteed)
      .filter((reward) => {
        const rate = reward.rate ?? 1; // 默认100%概率
        return Math.random() < rate;
      })
      .map((reward) => RewardFactory.createReward(reward));

    // 如果设置了掉落数量限制，则随机选择
    if (dropCount > 0 && probabilisticRewards.length > dropCount) {
      return this.randomSelect(probabilisticRewards, dropCount);
    }

    return probabilisticRewards;
  }

  /**
   * 权重策略：按权重比例随机选择指定数量的奖励
   */
  private static weightedRandom(pool: RewardPoolConfig): Reward[] {
    const { rewards } = pool;
    const dropCount = this.getActualDropCount(pool.dropCount);

    const weightedRewards = rewards.filter((reward) => !reward.guaranteed);

    if (weightedRewards.length === 0 || dropCount <= 0) {
      return [];
    }

    const result: Reward[] = [];
    const availableRewards = [...weightedRewards];

    for (let i = 0; i < dropCount && availableRewards.length > 0; i++) {
      // 计算总权重
      const totalWeight = availableRewards.reduce((sum, reward) => {
        return sum + (reward.weight ?? 1); // 默认权重为1
      }, 0);

      // 随机选择
      let random = Math.random() * totalWeight;
      let selectedIndex = -1;

      for (let j = 0; j < availableRewards.length; j++) {
        random -= availableRewards[j]?.weight ?? 1;
        if (random <= 0) {
          selectedIndex = j;
          break;
        }
      }

      // 如果因为浮点数精度没选中，选择最后一个
      if (selectedIndex === -1) {
        selectedIndex = availableRewards.length - 1;
      }

      const selectedConfig = availableRewards[selectedIndex];
      if (selectedConfig) {
        result.push(RewardFactory.createReward(selectedConfig));
        availableRewards.splice(selectedIndex, 1);
      }
    }

    return result;
  }

  /**
   * 固定策略：固定掉落指定数量的奖励（随机选择）
   */
  private static fixedDrops(pool: RewardPoolConfig): Reward[] {
    const { rewards } = pool;
    const dropCount = this.getActualDropCount(pool.dropCount);

    const fixedRewards = rewards.filter((reward) => !reward.guaranteed);

    if (fixedRewards.length === 0 || dropCount <= 0) {
      return [];
    }

    // 随机选择指定数量的奖励
    const selectedConfigs = this.randomSelect(fixedRewards, dropCount);
    return selectedConfigs.map((config) => RewardFactory.createReward(config));
  }

  /**
   * 根据策略处理概率掉落
   */
  private static calculateProbabilisticDrops(pool: RewardPoolConfig): Reward[] {
    switch (pool.dropStrategy) {
      case 'weighted':
        return this.weightedRandom(pool);
      case 'fixed':
        return this.fixedDrops(pool);
      default:
        return this.probabilityBased(pool);
    }
  }

  /**
   * 计算总掉落奖励
   */
  static calculateDrops(pool: RewardPoolConfig): Reward[] {
    const rewards: Reward[] = [];

    // 处理必掉奖励
    pool.rewards
      .filter((config) => config.guaranteed)
      .forEach((config) => {
        rewards.push(RewardFactory.createReward(config));
      });

    // 根据策略处理概率掉落
    const probabilisticRewards = this.calculateProbabilisticDrops(pool);
    rewards.push(...probabilisticRewards);

    return rewards;
  }

  /**
   * 计算掉落并合并相同奖励
   */
  static calculateDropsWithMerge(pool: RewardPoolConfig): Reward[] {
    const rewards = this.calculateDrops(pool);
    return RewardUtils.mergeRewards(rewards);
  }

  /**
   * 批量计算多个奖励池
   */
  static calculateMultiplePools(pools: RewardPoolConfig[]): Reward[] {
    const allRewards: Reward[] = [];

    for (const pool of pools) {
      const poolRewards = this.calculateDrops(pool);
      allRewards.push(...poolRewards);
    }

    return allRewards;
  }

  /**
   * 批量计算多个奖励池并合并相同奖励
   */
  static calculateMultiplePoolsWithMerge(pools: RewardPoolConfig[]): Reward[] {
    const allRewards = this.calculateMultiplePools(pools);
    return RewardUtils.mergeRewards(allRewards);
  }
}
